<?php
/*******************************************************************
 * MessageWriter.php
 * Copyright (C) 2006-2007 Midnight Coders, LLC
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is WebORB Presentation Server (R) for PHP.
 * 
 * The Initial Developer of the Original Code is Midnight Coders, LLC.
 * All Rights Reserved.
 ********************************************************************/

require_once(WebOrb . "Writer/ITypeWriter.php");
require_once(WebOrb . "Writer/NullWriter.php");
require_once(WebOrb . "Writer/NumberWriter.php");
require_once(WebOrb . "Writer/BooleanWriter.php");
require_once(WebOrb . "Writer/StringWriter.php");
require_once(WebOrb . "Writer/ArrayWriter.php");
require_once(WebOrb . "Writer/ObjectWriter.php");
require_once(WebOrb . "Writer/DateWriter.php");
require_once(WebOrb . "Writer/AMFMessageWriter.php");
require_once(WebOrb . "Writer/AMFHeaderWriter.php");
require_once(WebOrb . "Writer/AMFBodyWriter.php");
require_once(WebOrb . "Writer/ReferenceWriter.php");
require_once(WebOrb . "Writer/BoundPropertyBagWriter.php");
require_once(WebOrb . "Writer/BodyHolderWriter.php");
require_once(WebOrb . "Writer/StdClassWriter.php");
require_once(WebOrb . "Writer/ResourceWriter.php");
require_once(WebOrb . "Writer/DataTableAsListWriter.php");
require_once(WebOrb . "Writer/ORBDateTimeWriter.php");
require_once(WebOrb . "Writer/ArrayObjectWriter.php");

require_once(WebOrb . "Util/IDatabaseReader.php");

class MessageWriter
{

    private static $s_instance;
    private static $s_referenceWriter;
	private static $s_array = array();
	
    public static function getInstance()
    {
        if (is_null(self::$s_instance))
        {
            self::$s_instance = new MessageWriter();
            self::$s_referenceWriter = new ReferenceWriter();
        }

        return self::$s_instance;
    }

    public static function addTypeWriter($mappedType, ITypeWriter $writer)
    {
        self::getInstance()->m_writers[$mappedType] = $writer;
    }

    public static function writeObject(&$obj, IProtocolFormatter $formatter)
    {

        $writer = self::getWriter($obj);


        if (is_null($writer))
        {
            throw new Exception("Writer is not found.");
        }

        try
        {

          if($writer->isReferenceableType())
          {

            $referenceCache = $formatter->getReferenceCache();

            if($referenceCache == null)
            {
              $formatter->resetReferenceCache();

              $referenceCache = $formatter->getReferenceCache();
            }

            if( $referenceCache->hasObject( $obj ) )
            {
                self::$s_referenceWriter->write( $obj, $formatter );

                return;
            }
            else
            {
                $referenceCache->addObject( $obj );
            } 

          }

          $writer->write($obj, $formatter);
        }
        catch (Exception $ex)
        {
             Log::logException(LoggingConstants::EXCEPTION, "Unable to write object", $ex);
        }
    }

    private static function getWriter(&$param)
    {
        if (is_null($param))
        {
            return self::getInstance()->m_nullWriter;
        }
        else if (is_string($param))
        {
            return self::getInstance()->m_stringWriter;
        }
        else if (is_float($param) || is_int($param))
        {
            return self::getInstance()->m_numberWriter;
        }
        else if (is_array($param))
        {
        	if(count($param) == 0)
        		return self::getInstance()->m_arrayWriter;
        	
            $isDigitsFound = false;
            $isStringsFound = false;

            $keys = array_keys( $param );

            for( $i = 0; $i < sizeof( $keys ); $i++ )
            {
              if(is_string( $keys[ $i ] ))
              {
                $isStringsFound = true;
                break;
              }
              //else if( is_int( $keys[ $i ] ) )
              //  $isDigitsFound = true;
			  //
              //if( $isStringsFound && $isDigitsFound )
              //  break;
			  
            }

            if($isStringsFound)
                return self::getInstance()->m_mapWriter;

            return self::getInstance()->m_arrayWriter;
        }
        else if (is_bool($param))
        {
            return self::getInstance()->m_booleanWriter;
        }
        else if(is_resource($param))
        {
            return self::getInstance()->m_resourceWriter;
        }
        else if (is_object($param))
        {
            $type = get_class($param);

            if (isset(self::getInstance()->m_writers[$type]))
            {
                return self::getInstance()->m_writers[$type];
            }
            else
            {
                return self::getInstance()->m_defaultWriter;
            }
        }
        else
        {
            throw new Exception("This functionality is not realized yet.");
        }
    }

    private static function matchInterfaces($interfaces)
    {
        for ($index = 0; $index < count($interfaces); $index ++)
        {
            $writer = self::getWriter($interfaces[$index]);
            if (!is_null($writer))
            {
                return $writer;
            }

            $class = new ReflectionClass($interfaces[$index]);
            $classes = $class->getInterfaces();
            $subInterfaces = array();
            for ($classesIndex = 0; $classesIndex < count($classes); $classesIndex ++)
            {
                $subInterfaces[] = $classes[$classesIndex]->getName();
            }

            $writer = self::matchInterfaces($subInterfaces);
            if (!is_null($writer))
            {
                return $writer;
            }
        }
        return null;
    }

    private $m_defaultWriter;
    private $m_nullWriter;
    private $m_numberWriter;
    private $m_booleanWriter;
    private $m_stringWriter;
    private $m_messageWriter;
    private $m_headerWriter;
    private $m_bodyWriter;
    private $m_arrayWriter;
    private $m_dateWriter;
    private $m_orbdateWriter;
    private $m_writers = array();
    private $m_mapWriter;
    private $m_bodyHolderWriter;
    private $m_stdClassWriter;
    private $m_resourceWriter;
    private $m_dataTableAsListWriter;
    private $m_arrayObjectWriter;

    public function __construct()
    {
        $this->m_defaultWriter = new ObjectWriter();
        $this->m_nullWriter = new NullWriter();
        $this->m_numberWriter = new NumberWriter();
        $this->m_stringWriter = new StringWriter();
        $this->m_arrayWriter = new ArrayWriter();
        $this->m_dateWriter = new DateWriter( FALSE );
        $this->m_booleanWriter = new BooleanWriter();
        $this->m_messageWriter = new AMFMessageWriter();
        $this->m_headerWriter = new AMFHeaderWriter();
        $this->m_bodyWriter = new AMFBodyWriter();
        $this->m_mapWriter = new BoundPropertyBagWriter();
        $this->m_bodyHolderWriter = new BodyHolderWriter();
        $this->m_stdClassWriter = new StdClassWriter();
        $this->m_resourceWriter = new ResourceWriter();
        $this->m_dataTableAsListWriter = new DataTableAsListWriter();
		$this->m_orbdateWriter = new ORBDateTimeWriter( FALSE );
		$this->m_arrayObjectWriter = new ArrayObjectWriter();

        $this->m_writers['NULL'] = $this->m_nullWriter;
        $this->m_writers['integer'] = $this->m_numberWriter;
        $this->m_writers['double'] = $this->m_numberWriter;
        $this->m_writers['boolean'] = $this->m_booleanWriter;
        $this->m_writers['string'] = $this->m_stringWriter;
        $this->m_writers['Request'] = $this->m_messageWriter;
        $this->m_writers['Header'] = $this->m_headerWriter;
        $this->m_writers['Body'] = $this->m_bodyWriter;
        $this->m_writers['DateTime'] = $this->m_dateWriter;
        $this->m_writers['ORBDateTime'] = $this->m_orbdateWriter;
        $this->m_writers['array'] = $this->m_arrayWriter;
        $this->m_writers['object'] = $this->m_defaultWriter;
        $this->m_writers['BodyHolder'] = $this->m_bodyHolderWriter;
        $this->m_writers['stdClass'] = $this->m_stdClassWriter;
        $this->m_writers['DataTable'] = $this->m_dataTableAsListWriter;
        $this->m_writers['ArrayObject'] = $this->m_arrayObjectWriter;
    }

}

?>
